﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using ScrumTable.BL.DM.DataManagement;
using ScrumTable.Common.Monitoring;
using ScrumTable.Common.Security;
using ScrumTable.Common.Threading;
using ScrumTable.DL.Data;
using ScrumTable.DL.Data.Common;
using ScrumTable.DL.Data.Generic;
using ScrumTable.DL.Data.Generic.Cache;
using ScrumTable.DL.Data.Local;
using ScrumTable.DL.Data.Local.Test;

using WorkItemBucket = ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheWorkItem>;
using WorkItemSavePair = ScrumTable.DL.Data.Generic.Cache.CacheSavePair<ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheWorkItem>>;

#endregion

namespace ScrumTable.BL.DM.Test.DataManagement
{
    /// <summary>
    /// This is the test class for DomainCollection class.
    /// </summary>
    [TestClass]
    public class DomainCollectionTest
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private IDomainContext _context;
        private IDataContext _dataContext;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext { get; set; }

        private DomainCollection<Task> SampleTaskList
        {
            get { return _context.Projects[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Tasks; }
        }

        private DomainCollection<Project> SampleProjectList
        {
            get { return _context.Projects; }
        }

        private DomainCollection<UserStory> SampleStoryList
        {
            get { return SampleProjectList[Resources.Project1Id].Stories; }
        }

        #endregion

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new DomainCollectionTest instance.
        /// </summary>
        public DomainCollectionTest()
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Use TestInitialize to run code before running each test .
        /// </summary>
        [TestInitialize]
        public void TestInitialize()
        {
            _dataContext = DataConnectionFactory.Create(DataAssembly.FromAssembly(typeof(DataContext).Assembly));
            _context = DomainContextFactory.Create(_dataContext);
            _context.Connect(
                new SyncDataDispatcher(),
                new ConnectionInformationManager(ConnectionInformation.Empty),
                delegate { },
                Resources.GetProjectTestDataFilePath(TestContext.TestDir));

            _context.Projects[Resources.Project2Id].LoadData(new NullDataImportMonitor());
            _context.Projects[Resources.Project1Id].LoadData(new NullDataImportMonitor());
        }

        /// <summary>
        /// Use TestCleanup to run code after each test has run.
        /// </summary>
        [TestCleanup]
        public void TestCleanup()
        {
            _context.Dispose();
            _context = null;
        }

        [TestMethod]
        public void TestGetEnumerator()
        {
            Assert.IsNotNull(_context.Projects.GetEnumerator());
        }

        [TestMethod]
        public void TestChangedUserStory()
        {
            DomainCollectionEventMock<UserStory> domainCollectionEventMock = new DomainCollectionEventMock<UserStory>(
                SampleProjectList[Resources.Project1Id].Stories[Resources.UserStory1Id]);

            try
            {
                SampleProjectList[Resources.Project1Id].Stories[Resources.UserStory1Id].PropertyChanged += domainCollectionEventMock.PerformEventCheck;
                SampleProjectList[Resources.Project1Id].Stories[Resources.UserStory1Id].Name = Resources.UserStory1Id + "$";
            }
            finally 
            {
                SampleProjectList[Resources.Project1Id].Stories[Resources.UserStory1Id].PropertyChanged -= domainCollectionEventMock.PerformEventCheck;
            }
            Assert.IsTrue(domainCollectionEventMock.EventCalled);
        }

        [TestMethod]
        public void TestChangedTask()
        {
            DomainCollectionEventMock<Task> domainCollectionEventMock = new DomainCollectionEventMock<Task>(
                SampleTaskList[Resources.Task4Id]);

            try
            {
                SampleTaskList[Resources.Task4Id].PropertyChanged += domainCollectionEventMock.PerformEventCheck;
                SampleTaskList[Resources.Task4Id].Name = Resources.Task4Id + "$";
            }
            finally
            {
                SampleTaskList[Resources.Task4Id].PropertyChanged -= domainCollectionEventMock.PerformEventCheck;
            }
            Assert.IsTrue(domainCollectionEventMock.EventCalled);
        }

        [TestMethod]
        public void TestTryGetByChain()
        {
            UserStory story1 = _context.Projects[Resources.Project1Id].Stories[Resources.UserStory1Id];
            UserStory story2 = _context.Projects[Resources.Project2Id].Stories[Resources.UserStory2Id];
            UserStory story3 = _context.Projects[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id];
            UserStory story4 = _context.Projects[Resources.Project1Id].Stories[Resources.UserStory4Id];
            Task task4 = SampleTaskList[Resources.Task4Id];
            Project project1 = SampleProjectList[Resources.Project1Id];


            Assert.IsNotNull(_context.TryGetObjectByChain(story1.TypedIdChain));
            Assert.AreEqual(story1, _context.TryGetObjectByChain(story1.TypedIdChain));

            Assert.IsNotNull(_context.TryGetObjectByChain(story2.TypedIdChain));
            Assert.AreEqual(story2, _context.TryGetObjectByChain(story2.TypedIdChain));

            Assert.IsNotNull(_context.TryGetObjectByChain(story3.TypedIdChain));
            Assert.AreEqual(story3, _context.TryGetObjectByChain(story3.TypedIdChain));

            Assert.IsNotNull(_context.TryGetObjectByChain(story4.TypedIdChain));
            Assert.AreEqual(story4, _context.TryGetObjectByChain(story4.TypedIdChain));

            Assert.IsNotNull(_context.TryGetObjectByChain(task4.TypedIdChain));
            Assert.AreEqual(task4, _context.TryGetObjectByChain(task4.TypedIdChain));

            Assert.IsNotNull(_context.TryGetObjectByChain(project1.TypedIdChain));
            Assert.AreEqual(project1, _context.TryGetObjectByChain(project1.TypedIdChain));
        }

        [TestMethod]
        public void TestAddData()
        {
            int elementCount = SampleTaskList.Count;
            bool eventFired = false;

            DomainCollectionChangedEventHandler eventHandler =
                delegate(object sender, DomainCollectionChangedEventArgs e)
                {
                    if (e.Action == DomainCollectionChangedAction.Add)
                        eventFired = true;
                };

            SampleTaskList.CollectionChanged += eventHandler;
            SampleTaskList.AddNew();
            SampleTaskList.CollectionChanged -= eventHandler;

            Assert.AreEqual(elementCount + 1, SampleProjectList.Count);
            Assert.IsTrue(eventFired);
        }

        [TestMethod]
        public void TestAddStoryFromDataLayer()
        {
            int elementCount = SampleStoryList.Count;
            bool eventFired = false;

            DomainCollectionChangedEventHandler eventHandler =
                delegate(object sender, DomainCollectionChangedEventArgs e)
                {
                    if (e.Action == DomainCollectionChangedAction.Add)
                        eventFired = true;
                };

            SampleStoryList.CollectionChanged += eventHandler;
            string newStoryUniqueId = InsertNewStoryFromDataDriver();
            SampleStoryList.CollectionChanged -= eventHandler;

            Assert.AreEqual(elementCount + 1, SampleStoryList.Count);
            Assert.IsTrue(eventFired);
            Assert.IsTrue(SampleStoryList.ContainsId(newStoryUniqueId));
        }

        [TestMethod]
        public void TestAddTaskFromDataLayer()
        {
            string storyUniqueId = InsertNewStoryFromDataDriver();
            DomainCollection<Task> taskList = SampleStoryList[storyUniqueId].Tasks;

            int elementCount = taskList.Count;
            bool eventFired = false;

            DomainCollectionChangedEventHandler eventHandler =
                delegate(object sender, DomainCollectionChangedEventArgs e)
                {
                    if (e.Action == DomainCollectionChangedAction.Add)
                        eventFired = true;
                };

            taskList.CollectionChanged += eventHandler;
            string newTaskUniqueId = InsertNewTaskFromDataDriver();
            taskList.CollectionChanged -= eventHandler;

            Assert.AreEqual(elementCount + 1, taskList.Count);
            Assert.IsTrue(eventFired);
            Assert.IsTrue(taskList.ContainsId(newTaskUniqueId));
        }

        [TestMethod]
        public void TestMoveStoryOnDataLayer()
        {
            int oldElementCount = SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories.Count;
            int childTaskCount = SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Tasks.Count;
            int childBugCount = SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Bugs.Count;
            bool eventOnOldCollFired = false;

            int newElementCount = SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration2Id].Stories.Count;
            bool eventOnNewCollFired = false;

            DomainCollectionChangedEventHandler eventOldHandler =
                delegate(object sender, DomainCollectionChangedEventArgs e)
                {
                    if (e.Action == DomainCollectionChangedAction.Remove)
                    {
                        eventOnOldCollFired = true;
                    }
                };

            DomainCollectionChangedEventHandler eventNewHandler =
                delegate(object sender, DomainCollectionChangedEventArgs e)
                {
                    if (e.Action == DomainCollectionChangedAction.Add)
                    {
                        eventOnNewCollFired = true;
                    }
                };


            SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories.CollectionChanged += eventOldHandler;
            SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration2Id].Stories.CollectionChanged += eventNewHandler;

            try
            {
                MoveStoryOnDataDriver();
            }
            finally 
            {
                SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration2Id].Stories.CollectionChanged -= eventNewHandler;
                SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories.CollectionChanged -= eventOldHandler;
            }

            Assert.AreEqual(oldElementCount - 1, SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories.Count);
            Assert.AreEqual(newElementCount + 1, SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration2Id].Stories.Count);
            Assert.IsTrue(eventOnOldCollFired);
            Assert.IsTrue(eventOnNewCollFired);
            Assert.AreEqual(childTaskCount, SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration2Id].Stories[Resources.UserStory3Id].Tasks.Count);
            Assert.AreEqual(childBugCount, SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration2Id].Stories[Resources.UserStory3Id].Bugs.Count);
        }

        [TestMethod]
        public void TestRemoveChildOnDataLayer()
        {
            int childTaskCount = SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Tasks.Count;
            int childBugCount = SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Bugs.Count;
            bool eventOnCollFired = false;

            DomainCollectionChangedEventHandler eventOldHandler =
                delegate(object sender, DomainCollectionChangedEventArgs e)
                {
                    if (e.Action == DomainCollectionChangedAction.Remove)
                    {
                        eventOnCollFired = true;
                    }
                };

            SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Tasks.CollectionChanged += eventOldHandler;

            try
            {
                RemoveChildOnDataDriver();
            }
            finally
            {
                SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Tasks.CollectionChanged -= eventOldHandler;
            }

            Assert.AreEqual(childTaskCount - 1, SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Tasks.Count);
            Assert.AreEqual(childBugCount, SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Bugs.Count);
            Assert.IsTrue(eventOnCollFired);
        }

        [TestMethod]
        public void TestUnknownErrorEventFromDataLayer()
        {
            DomainBase syncError = null;
            DomainSyncErrorHandler syncErrorHandler = delegate(object sender, DomainSyncErrorArgs e)
                                                                 {
                                                                     Assert.IsTrue(e.IsUnkown);
                                                                     syncError = e.ErrorItem;
                                                                 };

            try
            {
                SampleProjectList.Context.SyncError += syncErrorHandler;
                RejectStoryOnDataDriver("Name", null, false, null);

                Assert.IsNotNull(syncError);
                Assert.AreEqual(Resources.UserStory3Id, syncError.Id);
            }
            finally 
            {
                SampleProjectList.Context.SyncError -= syncErrorHandler;
            }
        }

        [TestMethod]
        public void TestErrorEventWithFieldFromDataLayer()
        {
            const string invalidValue = "[New Name]";
            const string invalidField = "Name";
            object valueBefore = SampleProjectList[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Name;
            ScrumItemBase syncError = null;
            
            DomainSyncErrorHandler syncErrorHandler = delegate(object sender, DomainSyncErrorArgs e)
            {
                syncError = (ScrumItemBase)e.ErrorItem;

                Assert.IsFalse(e.IsUnkown);
                Assert.IsTrue(e.ErrorInfo.Count > 0);
                Assert.AreEqual(invalidField, e.ErrorInfo[0].FieldMappedName);
                Assert.AreEqual(invalidValue, e.ErrorInfo[0].Value);
                Assert.AreEqual(valueBefore, e.ErrorInfo[0].OriginalValue);
                Assert.AreEqual(e.ErrorInfo[0].Field, syncError.ItemType.FieldMeta.TryGetByName(invalidField));
                Assert.AreEqual(e.ErrorInfo[0].FieldErrorReason, DomainSyncErrorReason.TooLong);
                Assert.IsNotNull(e.ErrorInfo[0].FieldDisplayName);
                Assert.IsNull(e.ErrorInfo[0].Error);
            };

            try
            {
                SampleProjectList.Context.SyncError += syncErrorHandler;
                RejectStoryOnDataDriver(invalidField, invalidValue, true, null);

                Assert.IsNotNull(syncError);
                Assert.AreEqual(Resources.UserStory3Id, syncError.Id);
            }
            finally
            {
                SampleProjectList.Context.SyncError -= syncErrorHandler;
            }
        }

        [TestMethod]
        public void TestErrorEventWithExceptionFromDataLayer()
        {
            const string invalidValue = "[New Name]";
            const string invalidField = "Name";
            Exception error = new Exception("A Sample Exception");
            DomainBase syncError = null;

            DomainSyncErrorHandler syncErrorHandler = delegate(object sender, DomainSyncErrorArgs e)
            {
                Assert.IsFalse(e.IsUnkown);
                Assert.AreEqual(2, e.ErrorInfo.Count);

                Assert.IsNotNull(e.ErrorInfo[0].FieldDisplayName);
                Assert.IsNotNull(e.ErrorInfo[0].Value);
                Assert.IsNotNull(e.ErrorInfo[0].OriginalValue);
                Assert.IsNull(e.ErrorInfo[0].Error);

                Assert.IsNull(e.ErrorInfo[1].FieldDisplayName);
                Assert.IsNull(e.ErrorInfo[1].Value);
                Assert.IsNull(e.ErrorInfo[1].OriginalValue);
                Assert.AreEqual(error, e.ErrorInfo[1].Error);

                syncError = e.ErrorItem;
            };

            try
            {
                SampleProjectList.Context.SyncError += syncErrorHandler;
                RejectStoryOnDataDriver(invalidField, invalidValue, true, error);

                Assert.IsNotNull(syncError);
                Assert.AreEqual(Resources.UserStory3Id, syncError.Id);
            }
            finally
            {
                SampleProjectList.Context.SyncError -= syncErrorHandler;
            }
        }

        [TestMethod]
        public void TestLoadDataAsyncDeep()
        {
            TestLoadDataAsync(true);
        }

        [TestMethod]
        public void TestLoadDataAsyncFlat()
        {
            TestLoadDataAsync(false);
        }

        [TestMethod]
        public void TestLoadDataDeep()
        {
            TestLoadData(true);
        }

        [TestMethod]
        public void TestLoadDataFlat()
        {
            TestLoadData(false);
        }

        private void TestLoadDataAsync(bool deep)
        {
            int elementCount = SampleProjectList.Count;
            int subElementCount = SampleTaskList.Count;
            bool eventFired = false;

            DomainCollectionChangedEventHandler eventHandler =
                delegate(object sender, DomainCollectionChangedEventArgs e)
                {
                    if (e.Action == DomainCollectionChangedAction.Reset)
                        eventFired = true;
                };

            SampleProjectList.CollectionChanged += eventHandler;
            SampleProjectList.LoadDataAsync(deep, result => Assert.IsNull(((DataResult)result).Error));
            SampleProjectList.CollectionChanged -= eventHandler;

            Assert.AreEqual(elementCount, SampleProjectList.Count);
            Assert.AreEqual(subElementCount, SampleTaskList.Count);
            Assert.IsTrue(eventFired); 
        }

        private void TestLoadData(bool deep)
        {
            int elementCount = SampleProjectList.Count;
            int subElementCount = SampleTaskList.Count;
            bool eventFired = false;

            DomainCollectionChangedEventHandler eventHandler =
                delegate(object sender, DomainCollectionChangedEventArgs e)
                {
                    if (e.Action == DomainCollectionChangedAction.Reset)
                        eventFired = true;
                };

            SampleProjectList.CollectionChanged += eventHandler;
            SampleProjectList.LoadData(deep);
            SampleProjectList.CollectionChanged -= eventHandler;

            Assert.AreEqual(elementCount, SampleProjectList.Count);
            Assert.AreEqual(subElementCount, SampleTaskList.Count);
            Assert.IsTrue(eventFired);
        }


        #region Data Driver Actions

        private void RejectStoryOnDataDriver(string errorField, object errorValue, bool insertFieldError, Exception insertException)
        {
            if (!(_dataContext is GenericDataContextBase))
            {
                return;
            }

            GenericDataContextBase dataContext = (GenericDataContextBase)_dataContext;
            CacheDataChanges changes = dataContext.ExportChanges();
            CacheWorkItem wiToReject = (CacheWorkItem)dataContext.DataCache.CurrentProject.WorkItems.TryGetChild(
                Resources.UserStory3IdChain,
                Resources.UserStory3Id).Clone();

            object valueBefore = wiToReject[errorField];

            // this value assignment is invalid
            wiToReject[errorField] = errorValue;

            WorkItemBucket newBucket = new WorkItemBucket { Element = wiToReject };
            WorkItemSavePair newSavePair = new WorkItemSavePair(newBucket)
            {
                State = CacheSavePairState.Error
            };

            if (insertFieldError)
            {
                newSavePair.Errors.Add(new CacheSaveError(
                    null,
                    "Display Name: " + errorField,
                    errorField,
                    valueBefore,
                    errorValue,
                    CacheSaveErrorReason.TooLong));
            }

            if (insertException != null)
            {
                newSavePair.Errors.Add(new CacheSaveError(insertException));
            }
            
            changes.WorkItemsToSync[wiToReject.UniqueId] = newSavePair;
            changes.RemoveDirtyFlags();

            // this must fire the events
            dataContext.ImportChanges(changes);
        }

        private string InsertNewStoryFromDataDriver()
        {
            if (!(_dataContext is GenericDataContextBase))
            {
                return null;
            }

            GenericDataContextBase dataContext = (GenericDataContextBase) _dataContext;
            CacheDataChanges changes = dataContext.ExportChanges();

            // in mean time there's a new workitem on server side
            CacheWorkItem newSeverWi = changes.Project.OwnerContext.ElementFactory.CreateWorkItem(
                changes.Project.UserStoryType, 100, "WorkItem 100",
                1, CacheIdentity.DefaultId, changes.Project.UserStoryType.Workflow.TransitionStart,
                string.Empty, string.Empty, 
                new Dictionary<string, object>(), new CacheWorkItemLink[0], new CacheWorkItemLink[0],
                new CacheAttachment[0], new CacheWorkItemRevision[0]);

            WorkItemBucket newBucket = new WorkItemBucket { Element = newSeverWi };
            WorkItemSavePair newSavePair = new WorkItemSavePair(newBucket)
            {
                State = CacheSavePairState.Updated
            };

            changes.WorkItemsToSync[newSeverWi.UniqueId] = newSavePair;
            changes.RemoveDirtyFlags();

            // this must fire the events
            dataContext.ImportChanges(changes);
            return newSeverWi.UniqueId;
        }

        private string InsertNewTaskFromDataDriver()
        {
            if (!(_dataContext is GenericDataContextBase))
            {
                return null;
            }

            GenericDataContextBase dataContext = (GenericDataContextBase)_dataContext;
            CacheDataChanges changes = dataContext.ExportChanges();

            // in mean time there's a new workitem on server side
            CacheWorkItem newSeverWi = changes.Project.OwnerContext.ElementFactory.CreateWorkItem(
                changes.Project.TaskType, 101, "WorkItem 101",
                1, CacheIdentity.DefaultId, changes.Project.TaskType.Workflow.TransitionStart,
                string.Empty, string.Empty, 
                new Dictionary<string, object>(), new[] { changes.Project.OwnerContext.ElementFactory.CreateWorkItemLink(100 /* ref to new user story above */) },
                new CacheWorkItemLink[0], new CacheAttachment[0], new CacheWorkItemRevision[0]);
            
            WorkItemBucket newBucket = new WorkItemBucket { Element = newSeverWi };
            WorkItemSavePair newSavePair = new WorkItemSavePair(newBucket)
            {
                State = CacheSavePairState.Updated
            };

            changes.WorkItemsToSync[newSeverWi.UniqueId] = newSavePair;
            changes.RemoveDirtyFlags();

            // this must fire the events
            dataContext.ImportChanges(changes);
            return newSeverWi.UniqueId;
        }

        private void MoveStoryOnDataDriver()
        {
            if (!(_dataContext is GenericDataContextBase))
            {
                return;
            }

            GenericDataContextBase dataContext = (GenericDataContextBase)_dataContext;
            CacheDataChanges changes = dataContext.ExportChanges();
            CacheWorkItem wiToMove = (CacheWorkItem)dataContext.DataCache.CurrentProject.WorkItems.TryGetChild(
                Resources.UserStory3IdChain,
                Resources.UserStory3Id).Clone();

            // in mean time the workitem moved on server side
            wiToMove.Iteration = dataContext.DataCache.CurrentProject.Iterations.TryGetById(Resources.Iteration2Id);

            WorkItemBucket newBucket = new WorkItemBucket { Element = wiToMove };
            WorkItemSavePair newSavePair = new WorkItemSavePair(newBucket)
            {
                State = CacheSavePairState.Updated
            };

            changes.WorkItemsToSync[wiToMove.UniqueId] = newSavePair;
            changes.RemoveDirtyFlags();

            // this must fire the events
            dataContext.ImportChanges(changes);
        }

        private void RemoveChildOnDataDriver()
        {
            if (!(_dataContext is GenericDataContextBase))
            {
                return;
            }

            GenericDataContextBase dataContext = (GenericDataContextBase)_dataContext;
            CacheDataChanges changes = dataContext.ExportChanges();
            CacheWorkItem wiToRemoveChild = (CacheWorkItem)dataContext.DataCache.CurrentProject.WorkItems.TryGetChild(
                Resources.UserStory3IdChain,
                Resources.UserStory3Id).Clone();

            // in mean time the workitem child link has been removed on server side
            wiToRemoveChild.ChildLinks.Remove(wiToRemoveChild.ChildLinks.TryGetBy(lnk => lnk.RelatedUnqiueId == Resources.Task4Id));

            WorkItemBucket newBucket = new WorkItemBucket { Element = wiToRemoveChild };
            WorkItemSavePair newSavePair = new WorkItemSavePair(newBucket)
            {
                State = CacheSavePairState.Updated
            };

            changes.WorkItemsToSync[wiToRemoveChild.UniqueId] = newSavePair;
            changes.RemoveDirtyFlags();

            // this must fire the events
            dataContext.ImportChanges(changes);
        }

        #endregion

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}
